<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>

    <style>
        th,
        td {
            width: 150px;
            text-align: center;
        }

        td>input {
            background-color: white;
            text-align: center;
            border: none;
        }

        td>input.active {}

        #btnAdd {
            width: 100%;
            /* border: 1px solid #ccc */
        }
    </style>
</head>

<body>

    <!-- OOP版SuperTalble -->
    <script>
        class SuperTable {

            /* 构造器 */
            constructor(arr=[]) {
                // 接收并保存数据
                this.arr = arr

                // 保存数据列[name,age,gender,hobby]
                this.dataKeys = Object.keys(this.arr[0])

                /* 在页面上添加一个table */
                this.table = document.createElement("table")
                this.table.border = "1"
                document.body.appendChild(this.table)

                // 创建表头并完成初次渲染
                this.tableHeadStr = this.createTableHead()
                this.renderData(arr)

                // 找出元素
                this.btnAdd = this.table.querySelector("#btnAdd")

                // 绑定监听器（与用户交互）
                this.bindEventListeners()
            }

            /* 删除第index条数据 */
            deleteData(index, e) {
                // 删除数据
                this.arr.splice(index, 1)

                // 重新渲染(性能不OK)
                // this.renderData()

                // 找到第index个tr令其自裁
                // this.table.children[0].children[index + 1].remove()//性能更佳
                this.table.querySelectorAll("tr")[index + 1].remove()

                /* 更新各行的index */
                const trs = Array.from(this.table.querySelectorAll("tr")).slice(1)
                trs.forEach(
                    (tr, idx) => {
                        tr.querySelectorAll("button").forEach(
                            btn => btn.setAttribute("index", idx)
                        )
                    }
                )

            }

            /* 编辑第index条数据 */
            editData(index, e) {
                // 当前行的所有input
                const ips = e.target.parentNode.parentNode.querySelectorAll("input")
                console.log("编辑", index, e);

                if (e.target.innerText === "编辑") {
                    e.target.innerText = "确定"

                    // 激活第index行的input
                    ips.forEach(
                        ip => ip.removeAttribute("disabled")
                    )
                    ips[0].focus()
                }

                else {
                    e.target.innerText = "编辑"

                    // 更新数据
                    const item = this.arr[index]
                    ips.forEach(
                        ip => {
                            // 拿出input框中暗藏的key
                            const key = ip.getAttribute("key")
                            const value = ip.value

                            // 更新当前数据项
                            console.log("提交编辑", index, this.arr);
                            item[key] = value

                            // 为当前input添加disabled属性
                            ip.setAttribute("disabled", true)
                        }
                    )
                }

            }

            /* 生成一个所有key均为默认值的“空白”数据 */
            createDefaultData() {

                /* 构建一个空对象 */
                const copy = {}

                // 参考模板设置key-value，所有value使用""作为默认值
                for (let key of this.dataKeys) {
                    copy[key] = ""
                }

                // 返回空白数据
                console.log("copy=", copy);
                return copy
            }

            /* 绑定监听器 */
            bindEventListeners() {
                console.log("bindEventListeners", this);

                // 增加数据
                this.btnAdd.onclick = (e) => {
                    // 得到一条空白数据
                    const emptyData = this.createDefaultData()
                    this.arr.push(emptyData)

                    // 生成默认行tr的html字符串
                    const trStr = this.createTrStr(
                        // 生成默认空白数据
                        emptyData,

                        // 插入到arr的末尾
                        this.arr.length - 1,

                        // 配置：input不要打开disabled 不要最外层的tr包裹
                        { inputDisabled: false, useTrWrapper: false, btnEditText: "确定" }
                    )

                    /* 创建tr元素并设置innerHTML */
                    const tr = document.createElement("tr")
                    // tr.innerHTML = trStr.replace(/((<tr>)|(<\/tr>))/g, "")
                    tr.innerHTML = trStr

                    // 将新生成的tr插入到tbody【新增按钮】所在行的前面
                    // this.table.children[0]即tbody
                    this.table.children[0].insertBefore(
                        // 要插入的元素
                        tr,
                        // 插在谁之前（btnAdd所在的td所在的tr）
                        this.btnAdd.parentNode.parentNode
                    )
                    // 新增行中的第一列td中的input默认获得焦点
                    tr.children[0].children[0].focus()
                }

                // 将【删除】【编辑】按钮的事件委托给table
                this.table.onclick = (e) => {
                    /* 点击删除按钮 */
                    if (e.target.classList.contains("btnDel")) {
                        const index = e.target.getAttribute("index") * 1
                        console.log("删除", index);

                        // 删除第index条数据
                        this.deleteData(index, e)
                    }

                    /* 点击编辑按钮 */
                    if (e.target.classList.contains("btnEdit")) {
                        const index = e.target.getAttribute("index") * 1

                        // 编辑当前行
                        this.editData(index, e)
                    }
                }

            }

            /* 生成表头 */
            createTableHead() {
                // 制作表头
                let headStr = "<tr>"
                // 拼接若干个th（根据arr中的随便某条数据中的key）
                for (let key in this.arr[0]) {
                    headStr += `<th>${key}</th>`
                }
                // 在tr中拼接操作列 <th>操作</th>
                headStr += "<th>操作</th>"
                headStr += "</tr>"

                return headStr
            }

            /* 根据数据项生成tr字符串 */
            /**
             * item 数据项
             * index 插入到原始数据的位置（通常是采用arr.push）
             * option.inputDisabled 新生成的tr中的input是否开启【disabled】 默认开启
             * option.useTrWrapper 生成的tr字符串中是否有最外层的<tr>和</tr>
            */
            createTrStr(item, index, option = { inputDisabled: true, useTrWrapper: true, btnEditText: "编辑" }) {
                // 根据配置判断是否应该使用tr标签进行包裹
                let trStr = option.useTrWrapper ? "<tr>" : ""

                /* 根据obj的属性值生成td */
                for (let key in item) {
                    // 根据用户配置判断是否应该对input使用disabled
                    trStr += `<td><input ${option.inputDisabled ? 'disabled' : ''} key=${key} value=${item[key]}></td>`
                }
                // 丢【删除】【编辑】两个按钮到一个td中
                trStr += `<td>
                            <button class='btnDel' index='${index}'>删除</button>
                            <button class='btnEdit' index='${index}'>${option.btnEditText}</button>
                        </td>`

                // 根据配置判断是否应该使用tr标签进行包裹
                trStr += option.useTrWrapper ? "</tr>" : ""
                return trStr
            }

            /* 渲染数据 */
            renderData() {
                /* 用一个table显示arr */
                let tableHtml = ""

                //生成表头
                tableHtml += this.tableHeadStr

                /* 渲染数据 */
                this.arr.forEach(
                    (item, index) => {
                        tableHtml += this.createTrStr(item, index)
                    }
                )

                /* 渲染增加按钮 */
                tableHtml += `
                <tr>
                    <td colspan='5' border='0'>
                        <button id='btnAdd'>+</button>
                    </td>
                </tr>`

                // 将tableHtmlStr设置为table的innerHTML
                this.table.innerHTML = tableHtml
            }
        
            /* 导出数据 */
            exportData(){
                return JSON.stringify(this.arr).replace("\\","")
            }

        }

    </script>

    <script>
        var arr = [
            {
                name: 'Jack',
                age: 18,
                gender: '男',
                hobby: '女'
            }, {
                name: 'Rose',
                age: 20,
                gender: '女',
                hobby: '女'
            }, {
                name: '隆哥',
                age: 60,
                gender: '女',
                hobby: '女'
            }, {
                name: 'Top',
                age: 22,
                gender: '男',
                hobby: '女'
            }
        ]

        const st = new SuperTable(arr)
    </script>

</body>

</html>